/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "stub.h"
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "gmock/gmock-actions.h"
#include "NasPlugin.h"
#include "common/application/ApplicationServiceDataType.h"
#include "common/Path.h"
#include "define/Types.h"
#include "utils/PluginUtilities.h"
#include "config_reader/ConfigIniReader.h"
#include "PluginUtilities.h"
#include "SmbContextWrapper.h"
#include "HetroCommonStructs.h"
#include "device_access/dorado/DoradoBlock.h"
#include "ErrorCodes.h"
#include "job/ChannelManager.h"
#include <fstream>

using namespace std;
using namespace AppProtect;
using namespace Module;
using namespace PluginUtils;
using namespace NasChannelManager;

using ::testing::_;
using ::testing::AtLeast;
using testing::DoAll;
using testing::InitGoogleMock;
using ::testing::Return;
using ::testing::Sequence;
using ::testing::SetArgReferee;
using ::testing::Throw;

namespace {
    constexpr auto MP_SUCCESS = 0;
    const std::string META_FILE = "/opt/backup-copy-meta.json";
    const std::string CACHEFS_DIR= "/opt/backup-job/scan/meta/previous";
    const std::string CACHEFS_DIR_PARENT= "/opt/backup-job";
    struct MyCustomError {};
}

static bool SmbContextWrapper_Init_stub_success(void* obj)
{
    return true;
}

static bool SmbContextWrapper_SmbConnect_stub_success(void* obj)
{
    return true;
}

static bool SmbContextWrapper_Init_stub_failed(void* obj)
{
    return false;
}

static bool SmbContextWrapper_SmbConnect_stub_failed(void *obj)
{
    return false;
}

static string SmbGetClientGuid_stub(void* obj)
{
    return "";
}

static int CheckNfsAccessibility_stub_success(NasMountParams& param)
{
    return 0;
}

static int CheckNfsAccessibility_stub_failed(NasMountParams& param)
{
    return 1;
}

static int KinitTGTInner_stub_suc(){
    return Module::SUCCESS;
}

static int KinitTGTInner_stub_fail(){
    return Module::FAILED;
}

static bool Is_open_stub(){
    return true;
}

static bool AddSubJob_stub_success()
{
    return true;
}

static bool AddSubJob_stub_fail()
{
    return false;
}

static void JsonStringToStruct_stub_throw_unknow_err()
{
    throw MyCustomError{};
}

static void JsonStringToStruct_stub_throw_excp()
{
    throw std::runtime_error("错误");
}

static bool CreateMetaFile(std::string &jsonFileContent){
    std::ofstream writeFd {};
    int retryCnt = 0;

    do {
        writeFd.open(META_FILE.c_str(), std::ios::out);
        if (writeFd.is_open()) {
            break;
        }
        writeFd.close();
        sleep(NUMBER1);
    } while (++retryCnt < NUMBER3);

    if (!writeFd.is_open()) {
        return false;
    }

    writeFd << jsonFileContent;
    writeFd.close();
    return true;
}

static void DeleteMetaFile(){
    try {
        boost::filesystem::remove(META_FILE);
    } catch (const boost::filesystem::filesystem_error &e) {
        cout << "remove() exeption: " << e.code().message() << endl;
    }
}
class NasPluginTest : public testing::Test {
public:
    void SetUp();
    void TearDown();
    static void SetUpTestCase();
    static void TearDownTestCase();
    Stub stub;
};

static void returnVoidStub(void* obj)
{
    return;
}

static bool StubOperateIpsRule(std::vector<std::string>& ipList, const std::string& operation)
{
    return true;
}

void NasPluginTest::SetUp()
{
    stub.set(sleep, returnVoidStub);
    stub.set(ADDR(PluginUtils, OperateIpsRule), StubOperateIpsRule);
}

void NasPluginTest::TearDown(){}

void NasPluginTest::SetUpTestCase(){}

void NasPluginTest::TearDownTestCase(){}

/*
* 用例名称：AppInit
* 前置条件：无
* check点：日志log配置初始化
*/
TEST_F(NasPluginTest, AppInit)
{

    string logPath = Module::CPath::GetInstance().GetRootPath() + "/log";
    EXPECT_EQ(AppInit(logPath), Module::SUCCESS);

    string emptyPath = "";
    EXPECT_EQ(AppInit(emptyPath), Module::SUCCESS);
}

/*
* 用例名称：DiscoverHostCluster
* 前置条件：无
* check点：找主机集群，函数体为空
*/
TEST_F(NasPluginTest, DiscoverHostCluster)
{

    ApplicationEnvironment returnEnv; 
    ApplicationEnvironment appEnv;
    DiscoverHostCluster(returnEnv, appEnv);

    EXPECT_EQ(0, 0);
}

/*
* 用例名称：PauseJob
* 前置条件：无
* check点：暂停任务，函数体为空
*/
TEST_F(NasPluginTest, PauseJob)
{
    ActionResult returnValue;
    PauseJob(returnValue, "", "", "");

    EXPECT_EQ(0, 0);
}

/*
* 用例名称：AbortJob
* 前置条件：无
* check点：终止任务，函数体为空
*/
TEST_F(NasPluginTest, AbortJob){
    ActionResult returnValue;
    AbortJob(returnValue, "", "", "");
    EXPECT_EQ(0, 0);
}

/*
* 用例名称：QueryJobPermission
* 前置条件：无
* check点：查询任务权限，函数体为空
*/
TEST_F(NasPluginTest, QueryJobPermission){
    AppProtect::JobPermission returnJobPermission;
    Application application;
    QueryJobPermission(returnJobPermission, application);
    EXPECT_EQ(0, 0);
}

/*
* 用例名称：DiscoverAppCluster
* 前置条件：无
* check点：找应用集群，函数体为空，无返回值
*/
TEST_F(NasPluginTest, DiscoverAppCluster)
{

    ApplicationEnvironment returnEnv; 
    ApplicationEnvironment appEnv;
    Application application;
    DiscoverAppCluster(returnEnv, appEnv, application);

    EXPECT_EQ(0, 0);
}

/*
* 用例名称：CreateFactory
* 前置条件：无
* check点：创建工厂
*/
TEST_F(NasPluginTest, CreateFactory)
{
    JobFactoryBase* jobFactoryMap = CreateFactory();
    size_t jobSize = static_cast<CommonJobFactory*>(jobFactoryMap)->m_commonJobMap.size();
    bool ret = jobSize > 0;
    EXPECT_EQ(ret, true);  
}

/*
* 用例名称：CheckApplication
* 前置条件：无
* check点：检查请求，函数体为空
*/
TEST_F(NasPluginTest, CheckApplication)
{

    ActionResult returnValue;
    ApplicationEnvironment appEnv;
    Application application;
    CheckApplication(returnValue, appEnv, application);

    EXPECT_EQ(0, 0);
}


/*
* 用例名称：ListApplicationResource
* 前置条件：无
* check点：列出请求资源，函数体为空
*/
TEST_F(NasPluginTest, ListApplicationResource)
{

    vector<ApplicationResource> returnValue;
    ApplicationEnvironment appEnv;
    Application application;
    ApplicationResource parentResource;
    ListApplicationResource(returnValue, appEnv, application, parentResource);

    EXPECT_EQ(0, 0);
}

/*
* 用例名称：ListApplicationResourceV2
* 前置条件：无
* check点：列出请求资源，函数体为空
*/
TEST_F(NasPluginTest, ListApplicationResourceV2)
{

    ResourceResultByPage returnValue;
    ListResourceRequest request;
    ListApplicationResourceV2(returnValue, request);

    EXPECT_EQ(request.applications.empty(), true);
}

/*
* 用例名称：CheckBackupJobType
* 前置条件：无
* check点：检查恢复工作类型，函数体为空
*/
TEST_F(NasPluginTest, CheckBackupJobType)
{

    ActionResult returnValue;
    AppProtect::BackupJob job;
    HetroNasExtent temp;
    HetroLastCopyDetails backCopy;
    std::string jsonFileContent;

    CheckBackupJobType(returnValue, job);
    EXPECT_EQ(returnValue.code, 0);

    HetroDataLayOutExtend dataLayout;
    dataLayout.metaDataBackupType ="1";
    dataLayout.backupFormat="2";

    job.jobParam.backupType = AppProtect::BackupJobType::FULL_BACKUP;
    CheckBackupJobType(returnValue, job);
    EXPECT_EQ(returnValue.code, 0);

    job.jobParam.backupType = AppProtect::BackupJobType::INCREMENT_BACKUP;
    temp.m_protocol ="2";
    temp.m_filters="2";
    JsonHelper::StructToJsonString(temp, job.protectObject.extendInfo);
    CheckBackupJobType(returnValue, job);
    EXPECT_EQ(returnValue.code, 0);

    job.jobParam.backupType = AppProtect::BackupJobType::INCREMENT_BACKUP;
    temp.m_protocol ="1";
    temp.m_filters="2";
    JsonHelper::StructToJsonString(temp, job.protectObject.extendInfo);
    CheckBackupJobType(returnValue, job);
    EXPECT_EQ(returnValue.code, 0);

    job.jobParam.backupType = AppProtect::BackupJobType::INCREMENT_BACKUP;
    temp.m_protocol ="1";
    temp.m_filters="2";
    JsonHelper::StructToJsonString(temp, job.protectObject.extendInfo);
    JsonHelper::StructToJsonString(dataLayout, job.extendInfo);
    CheckBackupJobType(returnValue, job);
    EXPECT_EQ(returnValue.code, 200);

    job.jobParam.backupType = AppProtect::BackupJobType::INCREMENT_BACKUP;
    temp.m_protocol ="1";
    temp.m_filters="2";
    JsonHelper::StructToJsonString(temp, job.protectObject.extendInfo);
    JsonHelper::StructToJsonString(dataLayout, job.extendInfo);

    StorageRepository repoMeta;
    repoMeta.repositoryType = RepositoryDataType::META_REPOSITORY;
    repoMeta.path.push_back("/opt");
    job.repositories.push_back(repoMeta);
    CheckBackupJobType(returnValue, job);
    EXPECT_EQ(returnValue.code, 200);

    StorageRepository repoCache;
    repoCache.repositoryType = RepositoryDataType::CACHE_REPOSITORY;
    repoCache.path.push_back("/opt/");
    job.repositories.push_back(repoCache);
    CheckBackupJobType(returnValue, job);
    EXPECT_EQ(returnValue.code, 200);

    repoMeta.repositoryType = RepositoryDataType::META_REPOSITORY;
    CheckBackupJobType(returnValue, job);
    EXPECT_EQ(returnValue.code, 200);

    repoMeta.repositoryType = RepositoryDataType::META_REPOSITORY;
    backCopy.m_protocolVersion="111";
    backCopy.m_metadataBackupType="1";
    backCopy.m_lastBackupTime = 0;
    backCopy.m_backupFormat ="1";
    backCopy.m_backupFilter="1";
    JsonHelper::StructToJsonString(backCopy, jsonFileContent);
    CreateMetaFile(jsonFileContent);
    CheckBackupJobType(returnValue, job);
    DeleteMetaFile();
    EXPECT_EQ(returnValue.code, 200);

    repoMeta.repositoryType = RepositoryDataType::META_REPOSITORY;
    backCopy.m_protocolVersion="111";
    backCopy.m_metadataBackupType="1";
    backCopy.m_lastBackupTime = 1;
    backCopy.m_backupFormat ="1";
    backCopy.m_backupFilter="2";
    JsonHelper::StructToJsonString(backCopy, jsonFileContent);
    CreateMetaFile(jsonFileContent);
    CheckBackupJobType(returnValue, job);
    DeleteMetaFile();
    EXPECT_EQ(returnValue.code, 200);

    repoMeta.repositoryType = RepositoryDataType::META_REPOSITORY;
    backCopy.m_protocolVersion="111";
    backCopy.m_metadataBackupType="1";
    backCopy.m_lastBackupTime = 1;
    backCopy.m_backupFormat ="2";
    backCopy.m_backupFilter="2";
    JsonHelper::StructToJsonString(backCopy, jsonFileContent);
    RecurseCreateDirectory(CACHEFS_DIR);
    CreateMetaFile(jsonFileContent);
    CheckBackupJobType(returnValue, job);
    DeleteMetaFile();
    Remove(CACHEFS_DIR_PARENT);
    EXPECT_EQ(returnValue.code, 0);

    repoMeta.repositoryType = RepositoryDataType::META_REPOSITORY;
    jsonFileContent="{test}";
    RecurseCreateDirectory(CACHEFS_DIR);
    CreateMetaFile(jsonFileContent);
    CheckBackupJobType(returnValue, job);
    DeleteMetaFile();
    Remove(CACHEFS_DIR_PARENT);
    EXPECT_EQ(returnValue.code, 200);
}

/*
* 用例名称：AllowBackupInLocalNode
* 前置条件：无
* check点：允许在本地节点恢复，函数体为空
*/
TEST_F(NasPluginTest, AllowBackupInLocalNode)
{

    ActionResult returnValue;
    AppProtect::BackupJob job;
    AppProtect::BackupLimit::type limit; 
    AllowBackupInLocalNode(returnValue, job, limit);

    EXPECT_EQ(0, 0);
}

/*
* 用例名称：AllowBackupSubJobInLocalNode
* 前置条件：无
* check点：插入成功
*/
TEST_F(NasPluginTest, AllowBackupSubJobInLocalNodeSucc)
{

    ActionResult returnValue;
    AppProtect::BackupJob job;
    const AppProtect::SubJob subJob;
    HetroProtectAdvParms advParams{};
    advParams.m_channels = "10";
    JsonHelper::StructToJsonString(advParams, job.extendInfo);
    stub.set(ADDR(ChannelManager, AddSubJob), AddSubJob_stub_success);
    AllowBackupSubJobInLocalNode(returnValue, job, subJob);

    EXPECT_EQ(returnValue.code, 0);
}

/*
* 用例名称：AllowBackupSubJobInLocalNode
* 前置条件：无
* check点：插入失败
*/
TEST_F(NasPluginTest, AllowBackupSubJobInLocalNodeBusy)
{

    ActionResult returnValue;
    AppProtect::BackupJob job;
    const AppProtect::SubJob subJob;
    HetroProtectAdvParms advParams{};
    advParams.m_channels = "0";
    JsonHelper::StructToJsonString(advParams, job.extendInfo);
    stub.set(ADDR(ChannelManager, AddSubJob), AddSubJob_stub_fail);
    AllowBackupSubJobInLocalNode(returnValue, job, subJob);

    EXPECT_EQ(returnValue.code, 101);
}

/*
* 用例名称：AllowRestoreInLocalNode
* 前置条件：无
* check点：函数体为空
*/
TEST_F(NasPluginTest, AllowRestoreInLocalNode)
{

    ActionResult returnValue;
    const AppProtect::RestoreJob job;
     
    AllowRestoreInLocalNode(returnValue, job);

    EXPECT_EQ(0, 0);
}

/*
* 用例名称：AllowRestoreSubJobInLocalNode
* 前置条件：无
* check点：插入成功
*/
TEST_F(NasPluginTest, AllowRestoreSubJobInLocalNodeSucc)
{

    ActionResult returnValue;
    AppProtect::RestoreJob job;
    const AppProtect::SubJob subJob;
    HetroProtectAdvParms advParams{};
    advParams.m_channels = "10";
    JsonHelper::StructToJsonString(advParams, job.extendInfo);
    stub.set(ADDR(ChannelManager, AddSubJob), AddSubJob_stub_success);
    AllowRestoreSubJobInLocalNode(returnValue, job, subJob);

    EXPECT_EQ(returnValue.code, 0);
}

/*
* 用例名称：AllowBackupSubJobInLocalNode
* 前置条件：无
* check点：插入失败
*/
TEST_F(NasPluginTest, AllowRestoreSubJobInLocalNodeBusy)
{

    ActionResult returnValue;
    AppProtect::RestoreJob job;
    const AppProtect::SubJob subJob;
    HetroProtectAdvParms advParams{};
    advParams.m_channels = "0";
    JsonHelper::StructToJsonString(advParams, job.extendInfo);
    stub.set(ADDR(ChannelManager, AddSubJob), AddSubJob_stub_fail);
    AllowRestoreSubJobInLocalNode(returnValue, job, subJob);

    EXPECT_EQ(returnValue.code, 101);
}


/*
* 用例名称：CheckApplicationSmbSuccess
* 前置条件：无
* check点：函数体为空
*/
TEST_F(NasPluginTest, CheckApplicationSmbSuccess)
{
    ActionResult returnValue {};
    ApplicationEnvironment appEnv {};
    Application application {};

    application.auth.authType = AuthType::type::APP_PASSWORD;

    NasShareExtendInfo extendInfo {};
    extendInfo.ip = "11.4.5.14";
    extendInfo.shareMode = SMB_PROTOCOL;
    JsonHelper::StructToJsonString(extendInfo, application.extendInfo);

    stub.set(ADDR(SmbContextWrapper, Init), SmbContextWrapper_Init_stub_success);
    stub.set(ADDR(SmbContextWrapper, SmbConnect), SmbContextWrapper_SmbConnect_stub_success);
    stub.set(ADDR(SmbContextWrapper, SmbGetClientGuid), SmbGetClientGuid_stub);
    CheckApplication(returnValue, appEnv, application);
    EXPECT_EQ(returnValue.code, MP_SUCCESS);
}

/*
* 用例名称：CheckApplicationSmbFail
* 前置条件：无
* check点：函数体为空
*/
TEST_F(NasPluginTest, CheckApplicationSmbFail)
{
    ActionResult returnValue {};
    ApplicationEnvironment appEnv {};
    Application application {};

    application.auth.authType = AuthType::type::APP_PASSWORD;

    NasShareExtendInfo extendInfo {};
    extendInfo.ip = "11.4.5.14";
    extendInfo.shareMode = SMB_PROTOCOL;
    JsonHelper::StructToJsonString(extendInfo, application.extendInfo);

    stub.set(ADDR(SmbContextWrapper, Init), SmbContextWrapper_Init_stub_failed);
    stub.set(ADDR(SmbContextWrapper, SmbConnect), SmbContextWrapper_SmbConnect_stub_failed);
    stub.set(ADDR(SmbContextWrapper, SmbGetClientGuid), SmbGetClientGuid_stub);

    CheckApplication(returnValue, appEnv, application);
    EXPECT_EQ(returnValue.code, HomoErrorCode::INTERNAL_ERROR_CODE);

    extendInfo.shareMode = "-1";
    JsonHelper::StructToJsonString(extendInfo, application.extendInfo);
    CheckApplication(returnValue, appEnv, application);
    EXPECT_EQ(returnValue.code, HomoErrorCode::INTERNAL_ERROR_CODE);
}

/*
* 用例名称：CheckApplicationNfsSuccess
* 前置条件：无
* check点：函数体为空
*/
TEST_F(NasPluginTest, CheckApplicationNfsSuccess)
{
    ActionResult returnValue {};
    ApplicationEnvironment appEnv {};
    Application application {};

    NasShareExtendInfo extendInfo {};
    extendInfo.ip = "11.4.5.14";
    extendInfo.shareMode = NFS_PROTOCOL;
    JsonHelper::StructToJsonString(extendInfo, application.extendInfo);

    stub.set(CheckNfsAccessibility, CheckNfsAccessibility_stub_success);

    CheckApplication(returnValue, appEnv, application);
    EXPECT_EQ(returnValue.code, MP_SUCCESS);
}


/*
* 用例名称：CheckApplicationNfsFail
* 前置条件：无
* check点：函数体为空
*/
TEST_F(NasPluginTest, CheckApplicationNfsFail)
{
    ActionResult returnValue {};
    ApplicationEnvironment appEnv {};
    Application application {};

    NasShareExtendInfo extendInfo {};
    extendInfo.ip = "11.4.5.14";
    extendInfo.shareMode = NFS_PROTOCOL;
    JsonHelper::StructToJsonString(extendInfo, application.extendInfo);

    stub.set(CheckNfsAccessibility, CheckNfsAccessibility_stub_failed);

    CheckApplication(returnValue, appEnv, application);
    EXPECT_EQ(returnValue.code, HomoErrorCode::INTERNAL_ERROR_CODE);
}

/*
* 用例名称：GetChannelNum
* 前置条件：无
* check点：通道数为空
*/
TEST_F(NasPluginTest, GetChannelNumEmpty)
{
    ActionResult returnValue;
    AppProtect::BackupJob job;
    const AppProtect::SubJob subJob;
    AllowBackupSubJobInLocalNode(returnValue, job, subJob);
    EXPECT_EQ(returnValue.code, 0);
}

/*
* 用例名称：GetChannelNum
* 前置条件：无
* check点：通道数转换异常，未知异常
*/
TEST_F(NasPluginTest, GetChannelNumUnknowErr)
{
    ActionResult returnValue;
    AppProtect::BackupJob job;
    const AppProtect::SubJob subJob;

    HetroProtectAdvParms advParams{};
    advParams.m_channels = "10";
    JsonHelper::StructToJsonString(advParams, job.extendInfo);
    stub.set((bool(*)(const std::string& , HetroProtectAdvParms&))
        JsonHelper::JsonStringToStruct, JsonStringToStruct_stub_throw_unknow_err);
    AllowBackupSubJobInLocalNode(returnValue, job, subJob);

    EXPECT_EQ(returnValue.code, 0);
}

/*
* 用例名称：GetChannelNum
* 前置条件：无
* check点：通道数转换异常，标准异常
*/
TEST_F(NasPluginTest, GetChannelNumExceptionErr)
{
    ActionResult returnValue;
    AppProtect::BackupJob job;
    const AppProtect::SubJob subJob;

    HetroProtectAdvParms advParams{};
    advParams.m_channels = "10";
    JsonHelper::StructToJsonString(advParams, job.extendInfo);
    stub.set((bool(*)(const std::string& , HetroProtectAdvParms&))
        JsonHelper::JsonStringToStruct, JsonStringToStruct_stub_throw_excp);
    AllowBackupSubJobInLocalNode(returnValue, job, subJob);

    EXPECT_EQ(returnValue.code, 0);
}